HTML 77.2%
TypeScript 10.5%
Python 9.6%
JavaScript 2.5%
1import { ExternalLink } from 'lucide-react';2import type { Metadata } from 'next';3import Link from 'next/link';4import { notFound } from 'next/navigation';5import { CompareTrayBar } from '@/components/compare/compare-tray-bar';6import { ModelsTable } from '@/components/entity/blocks';7import { Tri } from '@/components/models/badges';8import { LICENCE_DIMENSIONS, OpennessBlock } from '@/components/models/openness-block';9import { EntityBadge } from '@/components/ui/badges';10import { Hint } from '@/components/ui/hint';11import { Pagination } from '@/components/ui/pagination';12import { Container, Note } from '@/components/ui/section';13import { ApiError, apiD1, safe } from '@/lib/api';14import { fmtInt } from '@/lib/format';15import { SITE_NAME, SITE_URL } from '@/lib/site';16import type { LicenseDetail, ModelOpenness } from '@/lib/types';1718type Params = { params: Promise<{ key: string }>; searchParams: Promise<{ offset?: string }> };19const LIMIT = 50;20export const revalidate = 3600;2122async function load(key: string, offset: number): Promise<LicenseDetail> {23 try {24 return await apiD1.license(key, LIMIT, offset);25 } catch (e) {26 if (e instanceof ApiError && e.notFound) notFound();27 throw e;28 }29}3031const tri = (v: boolean | null) => (v === true ? 'yes' : v === false ? 'no' : 'unknown');3233export async function generateMetadata({ params }: Params): Promise<Metadata> {34 const { key } = await params;35 const l = await safe(apiD1.license(key, 1));36 if (!l) return { title: 'Licence', robots: { index: false } };37 const title = `${l.label} — Model licence terms & models using it`;38 const description = `${l.label} (${l.category}${l.spdx ? `, SPDX ${l.spdx}` : ''}): commercial use ${tri(l.commercial_use)}, redistribution ${tri(l.redistribution)}, derivatives ${tri(l.derivatives)}, hosting restrictions ${tri(l.hosting_restrictions)}, acceptable-use policy ${tri(l.acceptable_use)}. ${fmtInt(l.models.total)} canonical models use it. ${SITE_NAME}.`.slice(0, 300);39 const canonical = `/licenses/${encodeURIComponent(l.key)}`;40 return { title, description, alternates: { canonical }, openGraph: { title: `${title} | ${SITE_NAME}`, description, url: `${SITE_URL}${canonical}`, type: 'article' } };41}4243/** Openness view derived from the licence permissions alone (weights downloadable · commercial · redistribution · derivatives). */44function opennessFromLicence(l: LicenseDetail): ModelOpenness {45 const category = !l.weights_downloadable ? 'proprietary' : l.commercial_use === false || l.hosting_restrictions === true || l.derivatives === false || l.category === 'community' || l.category === 'research-only' || l.category === 'responsible-ai' ? 'restricted-weights' : 'open-weights';46 const labels: Record<string, string> = { proprietary: 'Closed / proprietary', 'restricted-weights': 'Restricted weights', 'open-weights': 'Open weights' };47 const defs: Record<string, string> = {48 proprietary: 'Weights are not redistributable under this licence.',49 'restricted-weights': 'Weights downloadable, but the licence restricts commercial use, hosting, derivatives or field of use.',50 'open-weights': 'Weights downloadable under a permissive licence allowing commercial use; code or data may be missing.',51 };52 return {53 category,54 raw: l.key,55 label: labels[category] ?? category,56 definition: defs[category] ?? '',57 dimensions: { weights_available: l.weights_downloadable, source_code_available: null, training_code_available: null, training_data_disclosed: null, dataset_available: null, commercial_use_allowed: l.commercial_use, redistribution_allowed: l.redistribution, derivatives_allowed: l.derivatives },58 note: 'Derived from the licence text alone — a model under this licence may still publish code or data (see its page).',59 };60}6162export default async function LicensePage({ params, searchParams }: Params) {63 const { key } = await params;64 const { offset: off } = await searchParams;65 const offset = Math.max(0, Number(off) || 0);66 const l = await load(key, offset);67 const canonical = `/licenses/${encodeURIComponent(l.key)}`;68 const ld = { '@context': 'https://schema.org', '@type': 'CreativeWork', name: l.label, url: `${SITE_URL}${canonical}`, alternateName: l.aliases, sameAs: l.url ?? undefined, identifier: l.spdx ?? l.key, description: `${l.category} licence · commercial use ${tri(l.commercial_use)} · redistribution ${tri(l.redistribution)} · derivatives ${tri(l.derivatives)}` };6970 return (71 <Container wide>72 <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(ld) }} />73 <nav aria-label="Breadcrumb" className="pt-5 text-xs text-ink-3">74 <ol className="flex flex-wrap items-center gap-1.5">75 <li>76 <Link href="/" className="hover:text-ink">77 AI Atlas78 </Link>79 </li>80 <li aria-hidden>/</li>81 <li>82 <Link href="/licenses" className="hover:text-ink">83 Licences84 </Link>85 </li>86 <li aria-hidden>/</li>87 <li>88 <Link href={`/licenses?category=${encodeURIComponent(l.category)}`} className="hover:text-ink">89 {l.category}90 </Link>91 </li>92 <li aria-hidden>/</li>93 <li className="text-ink-2">{l.label}</li>94 </ol>95 </nav>96 <header className="pb-6 pt-4 md:pt-5" data-license-header>97 <div className="flex flex-wrap items-center gap-2">98 <EntityBadge type="license" />99 <span className="rounded-[3px] bg-surface-2 px-1.5 text-[11px] font-medium text-ink-2">{l.category}</span>100 {l.osi_approved && <span className="rounded-[3px] bg-positive-soft px-1.5 text-[11px] font-medium text-positive">OSI approved</span>}101 {l.spdx && <span className="mono text-xs text-ink-3">SPDX {l.spdx}</span>}102 </div>103 <div className="mt-3 flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between">104 <div className="min-w-0">105 <h1 className="display text-[30px] md:text-[44px]">{l.label}</h1>106 <p className="mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-[15px] text-ink-2">107 <span className="mono text-ink">{l.key}</span>108 {l.url && (109 <a href={l.url} target="_blank" rel="noopener noreferrer" className="inline-flex max-w-full min-w-0 items-center gap-1 text-ink-3 hover:text-accent">110 <span className="truncate">{l.url.replace(/^https?:\/\/(www\.)?/, '').replace(/\/$/, '').slice(0, 56)}</span> <ExternalLink className="size-3.5 shrink-0" aria-hidden />111 </a>112 )}113 </p>114 </div>115 <p className="tnum text-sm text-ink-2">116 <span className="font-semibold text-ink">{fmtInt(l.models.total)}</span> canonical models{' '}117 <Link href={`/models?license=${encodeURIComponent(l.key)}`} className="link">118 open in the terminal →119 </Link>120 </p>121 </div>122 </header>123124 <div className="grid gap-10 pb-16 lg:grid-cols-[minmax(0,1fr)_24rem]">125 <div className="min-w-0 space-y-10">126 <section>127 <p className="eyebrow mb-2">Dimensions</p>128 <dl className="kv" data-license-dimensions>129 {LICENCE_DIMENSIONS.map((d) => (130 <div key={d.key}>131 <dt>{d.label}</dt>132 <dd className="flex items-start justify-between gap-2 text-ink">133 <span>134 <Tri v={l[d.key] as boolean | null} yes={d.invert ? 'Yes — restrictions apply' : 'Yes'} no={d.invert ? 'None' : 'No'} />135 </span>136 <Hint text={d.hint} align="right" className="-my-1 shrink-0" />137 </dd>138 </div>139 ))}140 <div>141 <dt>Weights downloadable</dt>142 <dd className="text-ink">143 <Tri v={l.weights_downloadable} />144 </dd>145 </div>146 <div>147 <dt>OSI approved</dt>148 <dd className="text-ink">149 <Tri v={l.osi_approved} />150 </dd>151 </div>152 </dl>153 <Note className="mt-2">Read from the licence text by the ontology; a dash means the text is ambiguous and nothing is assumed. Not legal advice.</Note>154 </section>155 <section>156 <p className="eyebrow mb-2">What this means for openness</p>157 <OpennessBlock openness={opennessFromLicence(l)} compact />158 </section>159 <section id="models">160 <p className="eyebrow mb-2">161 Models under this licence <span className="tnum text-ink-3">{fmtInt(l.models.total)}</span>162 </p>163 <ModelsTable items={l.models.items} />164 <Pagination total={l.models.total} limit={LIMIT} offset={offset} makeHref={(o) => `${canonical}${o ? `?offset=${o}#models` : '#models'}`} className="mt-4" />165 </section>166 </div>167 <aside className="min-w-0 space-y-8">168 {l.aliases?.length > 0 && (169 <section>170 <p className="eyebrow mb-2">171 Aliases mapped to this licence <span className="tnum text-ink-3">{fmtInt(l.aliases.length)}</span>172 </p>173 <ul className="flex flex-wrap gap-1.5 text-xs">174 {l.aliases.map((a) => (175 <li key={a} className="mono border border-rule px-1.5 py-0.5 text-ink-2">176 {a}177 </li>178 ))}179 </ul>180 <Note className="mt-2">Raw labels stated by sources that the ontology maps to this canonical key.</Note>181 </section>182 )}183 <section>184 <p className="eyebrow mb-2">Category</p>185 <p className="text-sm text-ink-2">186 <Link href={`/licenses?category=${encodeURIComponent(l.category)}`} className="link">187 {l.category}188 </Link>{' '}189 — see the other licences of this category and the <Link href="/methodology" className="link">openness methodology</Link>.190 </p>191 </section>192 </aside>193 </div>194 <CompareTrayBar />195 </Container>196 );197}198